/*
 * facecl_context.cpp
 *
 *  Created on: 2016年2月25日
 *      Author: guyadong
 */
#include "facecl_context.h"
#include "assert_macros.h"
#include "cl-files/common_types.h"
#include "tls_var.h"
#include "intrinsic_wrapper.h"
#include "detect_cl.h"
namespace gdface{

cl::Context facecl_context::_initContext() {
	try {
		return cl::Context(CL_DEVICE_TYPE_GPU);
	} catch (cl::Error &e) {
		if (CL_DEVICE_NOT_FOUND != e.err())
			throw e;
		try {
			return cl::Context(CL_DEVICE_TYPE_CPU);
		} catch (cl::Error &e1) {
			if (CL_DEVICE_NOT_FOUND != e1.err())
				throw e1;
			return cl::Context(CL_DEVICE_TYPE_DEFAULT);
		}
	}
}

void facecl_context::showSupportedImageFormats(cl_mem_flags flags, cl_mem_object_type type,
		std::ostream& stream) {
	return cl_utilits::showSupportedImageFormats(m_context, flags, type, cout);
}
/* 将编译好的cl::Program 中所有的cl::Kernel加入m_kernels 映射 */
facecl_context& facecl_context::createKernels(const cl_utilits::build_param& param) {
	auto kernels = cl_utilits::createKernels(param);
	for(auto k:kernels){
		addKernel(k.first, k.second);
	}
	return *this;
}
const cl::Kernel& facecl_context::getKernel(const std::string& name) const {
	auto itor = m_kernels.find(name);
	if (itor == m_kernels.end())
		throw face_cl_exception(ERROR_STR("can't found kernel:").append(name));
	return itor->second;
}
facecl_context::facecl_context(const std::string &root):m_source_root(root){
	try{
		m_context = _initContext();
		m_device = m_context.getInfo<CL_CONTEXT_DEVICES>()[0];//第一个设备
		m_pe_num_per_cu=cl_utilits::kernel_preferred_work_group_size_multiple(m_device);
		auto device_type = m_device.getInfo<CL_DEVICE_TYPE>();
		cl_utilits::showDevices({m_device});
		m_gray_format = cl_utilits::getFittedImageFormatForGray(m_context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR);
		cout << "select format:" << cl_utilits::imageFormatToString(m_gray_format) << endl;
		
		if (CL_DEVICE_TYPE_CPU == device_type) {
			createKernels(builder().add_source_file(KERNEL_FILE_NAME(image_scaling)).build());
			// 用于计算float类型积分图,积方图//integral
			createKernels(builder().add_source_file(KERNEL_FILE_NAME(integral)).add_define("SRC_TYPE", "uchar").add_define("DST_TYPE", "float").add_define("INTEG_TYPE", integ_default).build());
			createKernels(builder().add_source_file(KERNEL_FILE_NAME(integral)).add_define("SRC_TYPE", "uchar").add_define("DST_TYPE", "float").add_define("INTEG_TYPE", integ_square).build());
			createKernels(builder().add_source_file(KERNEL_FILE_NAME(integral)).add_define("SRC_TYPE", "float").add_define("DST_TYPE", "float").add_define("INTEG_TYPE", integ_default).build());
			///// 用于计算人脸密度积分图//integral
			createKernels(builder().add_source_file(KERNEL_FILE_NAME(integral)).add_define("SRC_TYPE", "uchar").add_define("DST_TYPE", "ushort").add_define("INTEG_TYPE", integ_count).build());
			createKernels(builder().add_source_file(KERNEL_FILE_NAME(integral)).add_define("SRC_TYPE", "ushort").add_define("DST_TYPE", "ushort").add_define("INTEG_TYPE", integ_default).build());
			// 用于目标检测
			createKernels(builder().add_source_file(KERNEL_FILE_NAME(face_detect)).add_define("V_TYPE", 16).build());

		}
		else
		{
			createKernels(builder().add_source_file(KERNEL_FILE_NAME(image_scaling_gpu)).build());
			// 用于计算float类型积分图,积方图
			createKernels(builder().add_source_file(KERNEL_FILE_NAME(integral_gpu)).add_define("SRC_TYPE", "uchar").add_define("DST_TYPE", "float").add_define("INTEG_TYPE", integ_default).build());
			createKernels(builder().add_source_file(KERNEL_FILE_NAME(integral_gpu)).add_define("SRC_TYPE", "uchar").add_define("DST_TYPE", "float").add_define("INTEG_TYPE", integ_square).build());
			createKernels(builder().add_source_file(KERNEL_FILE_NAME(integral_gpu)).add_define("SRC_TYPE", "float").add_define("DST_TYPE", "float").add_define("INTEG_TYPE", integ_default).build());

			// 用于计算ulong类型积分图,积方图
			createKernels(builder().add_source_file(KERNEL_FILE_NAME(integral_gpu)).add_define("SRC_TYPE", "uchar").add_define("DST_TYPE", "ulong").add_define("INTEG_TYPE", integ_default).build());
			createKernels(builder().add_source_file(KERNEL_FILE_NAME(integral_gpu)).add_define("SRC_TYPE", "uchar").add_define("DST_TYPE", "ulong").add_define("INTEG_TYPE", integ_square).build());
			createKernels(builder().add_source_file(KERNEL_FILE_NAME(integral_gpu)).add_define("SRC_TYPE", "ulong").add_define("DST_TYPE", "ulong").add_define("INTEG_TYPE", integ_default).build());

			// 用于计算ulong类型积分图,积方图
			createKernels(builder().add_source_file(KERNEL_FILE_NAME(integral_gpu)).add_define("SRC_TYPE", "uchar").add_define("DST_TYPE", "double").add_define("INTEG_TYPE", integ_default).build());
			createKernels(builder().add_source_file(KERNEL_FILE_NAME(integral_gpu)).add_define("SRC_TYPE", "uchar").add_define("DST_TYPE", "double").add_define("INTEG_TYPE", integ_square).build());
			createKernels(builder().add_source_file(KERNEL_FILE_NAME(integral_gpu)).add_define("SRC_TYPE", "double").add_define("DST_TYPE","double").add_define("INTEG_TYPE", integ_default).build());

			///// 用于计算人脸密度积分图
			createKernels(builder().add_source_file(KERNEL_FILE_NAME(integral_gpu)).add_define("SRC_TYPE", "uchar") .add_define("DST_TYPE", "ushort").add_define("INTEG_TYPE", integ_count).build());
			createKernels(builder().add_source_file(KERNEL_FILE_NAME(integral_gpu)).add_define("SRC_TYPE", "ushort").add_define("DST_TYPE", "ushort").add_define("INTEG_TYPE", integ_default).build());
			// 用于目标检测
			createKernels(builder().add_source_file(KERNEL_FILE_NAME(face_detect_gpu)).build());
		}
		
		createKernels(builder().add_source_file(KERNEL_FILE_NAME(matrix_transpose)).add_define("SRC_TYPE", "uchar").build());
		// 所有kernel代码编译结束后释放编译器资源
		cl::Platform(m_device.getInfo<CL_DEVICE_PLATFORM>()).unloadCompiler();
	}catch(exception&e){
		cout<<e.what()<<endl;
		throw e;
	}
}
/*
 * 获取指定kernel的CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE参数
 */
std::pair<size_t, size_t> facecl_context::get_kernel_preferred_work_group_size_multiple(
		const std::string& name) const {
	auto& kernel = getKernel(name);
	auto kpwgsm = kernel.getWorkGroupInfo<CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE>(getDevice());
	auto shift_num = _bsr_int32_(uint32_t(kpwgsm));
	return std::make_pair(kpwgsm, shift_num);
}
/* 全局上下文对象 */
facecl_context global_facecl_context;
run_config_type global_run_config;

} /* namespace gdface */
